package com.yql.commonextend.utils;

import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.yql.commonextend.config.HttpConfig;
import org.apache.http.NameValuePair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.*;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.SocketAddress;
import java.util.*;

/**
 * @Author: chenjun
 * @Description: 调用外部接口工具类，可直接调用也可通过代理方式调用，并打印接口日志
 * @Date: 2021/8/2
 **/
public class HttpUtils{

    private static final Logger logger = LoggerFactory.getLogger("out-interface");//打印到独立日志文件

    /**
     * post请求数据，httpPost方式
     * @param urlString  请求地址
     * @param paramMap 请求参数map
     * @return
     */
    public static String post(String urlString, Map<String, Object> paramMap) {
        return doHttpRequest(true ,urlString, paramMap);
    }

    /**
     * get请求数据，httpGet方式
     * @param urlString  请求地址
     * @param paramMap 请求参数map
     * @return
     */
    public static String get(String urlString, Map<String, Object> paramMap) {
        return doHttpRequest(false ,urlString, paramMap);
    }

    /**
     * http请求方式
     * @param isPost
     * @param urlString
     * @param paramMap
     * @return
     */
    public static String doHttpRequest(boolean isPost, String urlString, Map<String, Object> paramMap) {
        String uuid = UUID.randomUUID().toString();
        long bTime = DateUtil.getCurrTimeMillis();
        StringBuilder sbf = new StringBuilder();
        sbf.append(uuid).append(" 调用外部接口开始,URL：").append(urlString).append("，参数：").append(JSONUtil.toJsonStr(paramMap))
                .append("，请求方式：").append(isPost ? "POST" : "GET");

        String result = "";
        if(isPost) {
            result = HttpUtil.post(urlString, paramMap);
        }else {
            result = HttpUtil.get(urlString, paramMap);
        }

        long eTime = DateUtil.getCurrTimeMillis();
        StringBuilder sbd = new StringBuilder();
        sbd.append(uuid).append(" 调用外部接口结束，共耗时：").append(eTime - bTime).append("毫秒")
                .append("，返回结果：").append(result);
        logger.info(sbd.toString());

        return result;
    }

    /**
     * post提交数据，rest方式
     * @param urlString  请求地址
     * @param param 请求参数json，如 {"params":"kK6UUqQte5bWPPYUy5OFc4i5PhsDatFRzd+7vwq5ljc="}
     * @return
     */
    public static JSONObject post(String urlString, String param){
        JSONObject result = doRestRequest(urlString, param, HttpMethod.POST, HttpConfig.getIsLocal(),
                HttpConfig.getHost(), HttpConfig.getPort() , HttpConfig.getReadTimeout(), HttpConfig.getReadTimeout());
        return result;
    }

    /**
     * post提交数据，rest方式  请求头信息为： application/x-www-form-urlencoded
     * @param urlString  请求地址
     * @param param 请求参数json，如 {"params":"kK6UUqQte5bWPPYUy5OFc4i5PhsDatFRzd+7vwq5ljc="}
     * @return
     */
    public static JSONObject postForm(String urlString, Map<String, Object> param){
        JSONObject result = doRestRequestForm(urlString, param, HttpMethod.POST, HttpConfig.getIsLocal(),
                HttpConfig.getHost(), HttpConfig.getPort() , HttpConfig.getReadTimeout(), HttpConfig.getReadTimeout());
        return result;
    }




    /**
     * get请求数据，rest方式
     * @param urlString  请求地址
     * @param param 请求参数url，如?params=111&aaa=222&bbb=333
     * @return
     */
    public static JSONObject get(String urlString, String param){
        JSONObject result = doRestRequest(urlString, param, HttpMethod.GET, HttpConfig.getIsLocal(),
                HttpConfig.getHost(), HttpConfig.getPort() , HttpConfig.getReadTimeout(), HttpConfig.getReadTimeout());
        return result;
    }

    /**
     * 直接调用或代理方式调用
     * @param urlString  访问地址
     * @param param  参数
     * @param method post或get
     * @param isLocal 是否代理调用
     * @param host   代理ip
     * @param port   代理端口
     * @param readTimeout
     * @param connectTimeout
     * @return
     */
    private static JSONObject doRestRequest(String urlString, String param ,HttpMethod method, boolean isLocal, String host, String port,
                                 String readTimeout, String connectTimeout) {

        String uuid = UUID.randomUUID().toString();
        long bTime = DateUtil.getCurrTimeMillis();
        StringBuilder sbf = new StringBuilder();
        sbf.append(uuid).append(" 调用外部接口开始,URL：").append(urlString).append("，参数：").append(param)
                .append("，请求方式：").append(method.toString());
        if(isLocal){
            sbf.append("，本地调用");
        }else{
            sbf.append("，代理调用：").append(host).append(port);
        }
        logger.info(sbf.toString());

        RestTemplate restTemplate = null;
        if (!isLocal) {//通过代理IP、端口调用
            SimpleClientHttpRequestFactory httpRequestFactory = new SimpleClientHttpRequestFactory();
            httpRequestFactory.setReadTimeout(Integer.parseInt(readTimeout));
            httpRequestFactory.setConnectTimeout(Integer.parseInt(connectTimeout));
            SocketAddress socketAddress = new InetSocketAddress(host, Integer.parseInt(port));
            Proxy proxy = new Proxy(Proxy.Type.HTTP, socketAddress);
            httpRequestFactory.setProxy(proxy);
            restTemplate = new RestTemplate(httpRequestFactory);
        }else{
            restTemplate = new RestTemplate();
        }
        ResponseEntity<String> exchange = null;
        if(method == HttpMethod.POST){
            //post提交将参数放入body中
            HttpEntity requestEntity = new HttpEntity<>(param, getAuthHeaders());
            exchange = restTemplate.exchange(urlString, method, requestEntity, String.class);
        }else{
            //get提交将参数拼接到path之后
            HttpEntity requestEntity = new HttpEntity<>(getAuthHeaders());
            exchange = restTemplate.exchange(urlString + param, method, requestEntity, String.class);
        }
        JSONObject jsonObject = JSONUtil.parseObj(exchange.getBody());

        long eTime = DateUtil.getCurrTimeMillis();
        StringBuilder sbd = new StringBuilder();
        sbd.append(uuid).append(" 调用外部接口结束，共耗时：").append(eTime - bTime).append("毫秒")
                .append("，返回结果：").append(jsonObject.toString());
        logger.info(sbd.toString());

        return jsonObject;
    }

    private static JSONObject doRestRequestForm(String urlString, Map<String, Object> param ,HttpMethod method, boolean isLocal, String host, String port,
                                         String readTimeout, String connectTimeout){
        String uuid = UUID.randomUUID().toString();
        long bTime = DateUtil.getCurrTimeMillis();
        StringBuilder sbf = new StringBuilder();
        sbf.append(uuid).append(" 调用外部接口开始,URL：").append(urlString).append("，参数：").append(param)
                .append("，请求方式：").append(method.toString());
        if(isLocal){
            sbf.append("，本地调用");
        }else{
            sbf.append("，代理调用：").append(host).append(port);
        }
        logger.info(sbf.toString());

        RestTemplate restTemplate = null;
        if (!isLocal) {//通过代理IP、端口调用
            SimpleClientHttpRequestFactory httpRequestFactory = new SimpleClientHttpRequestFactory();
            httpRequestFactory.setReadTimeout(Integer.parseInt(readTimeout));
            httpRequestFactory.setConnectTimeout(Integer.parseInt(connectTimeout));
            SocketAddress socketAddress = new InetSocketAddress(host, Integer.parseInt(port));
            Proxy proxy = new Proxy(Proxy.Type.HTTP, socketAddress);
            httpRequestFactory.setProxy(proxy);
            restTemplate = new RestTemplate(httpRequestFactory);
        }else{
            restTemplate = new RestTemplate();
        }
        ResponseEntity<String> exchange = null;
        if(method == HttpMethod.POST){
            //post提交将参数放入body中
            HttpEntity requestEntity = new HttpEntity<>(mapTransfer(param), getFormAuthHeaders());
            exchange = restTemplate.exchange(urlString, method, requestEntity, String.class);
        }else{
            //get提交将参数拼接到path之后
            HttpEntity requestEntity = new HttpEntity<>(getFormAuthHeaders());
            exchange = restTemplate.exchange(urlString + mapTransfer(param), method, requestEntity, String.class);
        }
        JSONObject jsonObject = JSONUtil.parseObj(exchange.getBody());

        long eTime = DateUtil.getCurrTimeMillis();
        StringBuilder sbd = new StringBuilder();
        sbd.append(uuid).append(" 调用外部接口结束，共耗时：").append(eTime - bTime).append("毫秒")
                .append("，返回结果：").append(jsonObject.toString());
        logger.info(sbd.toString());
        return jsonObject;
    }

    /**
     * 设置调用时header信息
     * @return
     */
    private static HttpHeaders getAuthHeaders() {
        HttpHeaders headers = new HttpHeaders();
        MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8");
        headers.setContentType(type);
        headers.add("Accept", MediaType.APPLICATION_JSON.toString());
        return headers;
    }


    /**
     * 设置调用时header信息
     * @return
     */
    private static HttpHeaders getFormAuthHeaders() {
        HttpHeaders headers = new HttpHeaders();
        MediaType type = MediaType.parseMediaType("application/x-www-form-urlencoded; charset=UTF-8");
        headers.setContentType(type);
        return headers;
    }

    private static MultiValueMap<String, Object> mapTransfer(Map<String, Object> paramMap){
        MultiValueMap<String, Object> postParameters = new LinkedMultiValueMap<>();
        if (null != paramMap && paramMap.size() > 0) {
            List<NameValuePair> nvps = new ArrayList<NameValuePair>();
            // 通过map集成entrySet方法获取entity
            Set<Map.Entry<String, Object>> entrySet = paramMap.entrySet();
            // 循环遍历，获取迭代器
            Iterator<Map.Entry<String, Object>> iterator = entrySet.iterator();
            while (iterator.hasNext()) {
                Map.Entry<String, Object> mapEntry = iterator.next();
                //nvps.add(new BasicNameValuePair(mapEntry.getKey(), ));
                postParameters.add(mapEntry.getKey(), mapEntry.getValue().toString());
            }
        }
        return postParameters;
    }
}
